#include "datacenter.h"

namespace model
{
DataCenter *DataCenter::instance = nullptr;

DataCenter *DataCenter::getInstance()
{
    if (instance == nullptr)
    {
        instance = new DataCenter();
    }
    return instance;
}

DataCenter::~DataCenter()
{
    // C++ 标准中规定 delete nullptr 是合法行为，所以不必判空
    delete _myself;
    delete _friendList;
    delete _chatSessionList;
    delete _memberList;
    delete _applyList;
    delete _recentMessages;
    delete _unreadMessageCount;
    delete _searchUserResult;
    delete _searchMessageResult;
}

DataCenter::DataCenter() : _netClient(this)
{
    // 为了使用 nullptr 表示非法状态，其他的 QList 类型属性不在此处实例化
    _recentMessages     = new QHash<QString, QList<Message>>();
    _memberList         = new QHash<QString, QList<UserInfo>>();
    _unreadMessageCount = new QHash<QString, int>();

    // 加载数据
    loadDataFile();
}

void DataCenter::initDataFile()
{
    // 获取系统 appData 作为父目录
    QString basePath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
    QString filePath = basePath + "/chatClient.json";
    LOG() << "filePath=" << filePath;

    QDir dir;
    if (!dir.exists(basePath))
    {
        dir.mkpath(basePath);
    }

    // 写方式打开，并写入初始内容
    QFile file(filePath);
    if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
    {
        LOG() << "打开文件失败！" << file.errorString();
        return;
    }
    // 写入初始内容
    QString data = "{\n\n}";
    file.write(data.toUtf8());
    file.close();
}

void DataCenter::saveDataFile()
{
    QString filePath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/ChatClient.json";
    QFile file(filePath);
    if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
    {
        LOG() << "打开文件失败！" << file.errorString();
        return;
    }

    // 按照 json 的格式写入数据
    QJsonObject jsonObj;
    jsonObj["loginSessionId"] = _loginSessionId;

    QJsonObject jsonUnread;
    for (auto it = _unreadMessageCount->begin(); it != _unreadMessageCount->end(); it++)
    {
        jsonUnread[it.key()] = it.value();
    }
    jsonObj["unread"] = jsonUnread;

    QJsonDocument jsonDoc(jsonObj);
    QString s = jsonDoc.toJson();
    file.write(s.toUtf8());

    file.close();
}

// 加载文件，在 DataCenter 实例化时调用
void DataCenter::loadDataFile()
{
    QString filePath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/ChatClient.json";

    // 判断文件是否存在，不存在则初始化，并创建出空白的 json 文件
    QFileInfo fileInfo(filePath);
    if (!fileInfo.exists())
    {
        initDataFile();
    }

    // 读方式打开文件
    QFile file(filePath);
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
    {
        LOG() << "文件打开失败！" << file.errorString();
        return;
    }

    // 读到文件内容，解析为 json 对象
    QJsonDocument jsonDoc = QJsonDocument::fromJson(file.readAll());
    if (jsonDoc.isNull())
    {
        LOG() << "解析 JSON 文件失败！JSON 文件格式有误！";
        file.close();
        return;
    }
    QJsonObject jsonObj   = jsonDoc.object();
    this->_loginSessionId = jsonObj["loginSessionId"].toString();
    LOG() << "loginSessionId=" << this->_loginSessionId;

    this->_unreadMessageCount->clear();
    QJsonObject jsonUnread = jsonObj["unread"].toObject();
    for (auto it = jsonUnread.begin(); it != jsonUnread.end(); it++)
    {
        this->_unreadMessageCount->insert(it.key(), it.value().toInt());
    }
    file.close();
}

void DataCenter::initWebsocket() { _netClient.initWebsocket(); }

void DataCenter::getMyselfAsync() { _netClient.getMyself(_loginSessionId); }

UserInfo *DataCenter::getMyself() { return _myself; }

void DataCenter::resetMyself(std::shared_ptr<MicroIMS::GetUserInfoRsp> resp)
{
    if (_myself == nullptr)
        _myself = new model::UserInfo();
    const MicroIMS::UserInfo &userInfo = resp->userInfo();
    _myself->load(userInfo);
}

void DataCenter::getFriendListAsync() { _netClient.getFriendList(_loginSessionId); }

QList<UserInfo> *DataCenter::getFriendList() { return _friendList; }

void DataCenter::resetFriendList(std::shared_ptr<MicroIMS::GetFriendListRsp> resp)
{
    if (_friendList == nullptr)
    {
        _friendList = new QList<UserInfo>();
    }
    _friendList->clear();

    QList<MicroIMS::UserInfo> &friendListPB = resp->friendList();
    for (auto &f : friendListPB)
    {
        UserInfo userInfo;
        userInfo.load(f);
        _friendList->push_back(userInfo);
    }
}

void DataCenter::getChatSessionListAsync() { _netClient.getChatSessionList(_loginSessionId); }

QList<ChatSessionInfo> *DataCenter::getChatSessionList() { return _chatSessionList; }

void DataCenter::resetChatSessionList(std::shared_ptr<MicroIMS::GetChatSessionListRsp> resp)
{
    if (_chatSessionList == nullptr)
    {
        _chatSessionList = new QList<ChatSessionInfo>();
    }
    _chatSessionList->clear();

    auto &chatSessionListPB = resp->chatSessionInfoList();
    for (auto &c : chatSessionListPB)
    {
        ChatSessionInfo chatSessionInfo;
        chatSessionInfo.load(c);
        _chatSessionList->push_back(chatSessionInfo);
    }
}

void DataCenter::getApplyListAsync() { _netClient.getApplyList(_loginSessionId); }

QList<UserInfo> *DataCenter::getApplyList() { return _applyList; }

void DataCenter::resetApplyList(std::shared_ptr<MicroIMS::GetPendingFriendEventListRsp> resp)
{
    if (_applyList == nullptr)
    {
        _applyList = new QList<UserInfo>();
    }
    _applyList->clear();

    auto &eventList = resp->event();
    for (auto &event : eventList)
    {
        UserInfo userInfo;
        userInfo.load(event.sender());
        _applyList->push_back(userInfo);
    }
}

void DataCenter::getRecentMessageListAsync(const QString &chatSessionId, bool updateUI)
{
    _netClient.getRecentMessageList(_loginSessionId, chatSessionId, updateUI);
}

QList<Message> *DataCenter::getRecentMessageList(const QString &chatSessionId)
{
    if (!_recentMessages->contains(chatSessionId))
    {
        return nullptr;
    }
    return &(*_recentMessages)[chatSessionId];
}

void DataCenter::resetRecentMessageList(const QString &chatSessionId, std::shared_ptr<MicroIMS::GetRecentMsgRsp> resp)
{
    // 拿到 chatSessionId 对应的消息列表，并清空
    // 此处必须要使用引用类型才能拿到哈希表的内容，否则拿到的是内容的拷贝
    QList<Message> &messageList = (*_recentMessages)[chatSessionId];
    messageList.clear();

    for (auto &m : resp->msgList())
    {
        Message message;
        message.load(m);
        messageList.push_back(message);
    }
}

ChatSessionInfo *DataCenter::findChatSessionById(const QString &chatSessionId)
{
    if (_chatSessionList == nullptr)
    {
        return nullptr;
    }
    for (auto &info : *_chatSessionList)
    {
        if (info.chatSessionId == chatSessionId)
        {
            return &info;
        }
    }
    return nullptr;
}

ChatSessionInfo *DataCenter::findChatSessionByUserId(const QString &userId)
{
    if (_chatSessionList == nullptr)
    {
        return nullptr;
    }
    for (auto &info : *_chatSessionList)
    {
        if (info.userId == userId)
        {
            return &info;
        }
    }
    return nullptr;
}

void DataCenter::setCurrentChatSessionId(const QString &chatSessionId) { this->_currentChatSessionId = chatSessionId; }

QString &DataCenter::getCurrentChatSessionId() { return this->_currentChatSessionId; }

void DataCenter::topChatSessionInfo(const ChatSessionInfo &chatSessionInfo)
{
    if (_chatSessionList == nullptr)
    {
        return;
    }

    // 1. 把这个元素从列表中找到
    auto it = _chatSessionList->begin();
    for (; it != _chatSessionList->end(); it++)
    {
        if (it->chatSessionId == chatSessionInfo.chatSessionId)
        {
            break;
        }
    }
    if (it == _chatSessionList->end())
    {
        // 循环没有匹配到结果，正常不会进入这个逻辑
        return;
    }

    // 2. 备份这个元素然后删掉
    ChatSessionInfo backup = chatSessionInfo;
    _chatSessionList->erase(it);

    // 3. 把备份的元素插入到头部
    _chatSessionList->push_front(backup);
}

void DataCenter::sendTextMessageAsync(const QString &chatSessionId, const QString &content)
{
    _netClient.sendMessage(_loginSessionId, chatSessionId, MessageType::TEXT_TYPE, content.toUtf8(), "");
}

void DataCenter::addMessage(const Message &message)
{
    QList<Message> &messageList = (*_recentMessages)[message.chatSessionId];
    messageList.push_back(message);
}

void DataCenter::clearUnread(const QString &chatSessionId)
{
    (*_unreadMessageCount)[chatSessionId] = 0;
    // 手动保存到文件
    saveDataFile();
}

void DataCenter::addUnread(const QString &chatSessionId)
{
    ++(*_unreadMessageCount)[chatSessionId];
    // 手动保存到文件
    saveDataFile();
}

int DataCenter::getUnread(const QString &chatSessionId) { return (*_unreadMessageCount)[chatSessionId]; }

void DataCenter::changeNicknameAsync(const QString &nickname) { _netClient.changeNickname(_loginSessionId, nickname); }

void DataCenter::resetNickname(const QString &nickname)
{
    if (_myself == nullptr)
    {
        return;
    }
    _myself->nickname = nickname;
}

void DataCenter::changeDescriptionAsync(const QString &desc) { _netClient.changeDescription(_loginSessionId, desc); }

void DataCenter::resetDescription(const QString &desc)
{
    if (_myself == nullptr)
    {
        return;
    }
    _myself->description = desc;
}

void DataCenter::getVerifyCodeAsync(const QString &phone) { _netClient.getVerifyCode(phone); }

const QString &DataCenter::getVerifyCodeId() { return _currentVerifyCodeId; }

void DataCenter::resetVerifyCodeId(const QString &verifyCodeId) { _currentVerifyCodeId = verifyCodeId; }

void DataCenter::changePhoneAsync(const QString &phone, const QString &verifyCodeId, const QString &verifyCode)
{
    _netClient.changePhone(_loginSessionId, phone, verifyCodeId, verifyCode);
}

void DataCenter::resetPhone(const QString &phone)
{
    if (_myself == nullptr)
    {
        return;
    }
    _myself->phone = phone;
}

void DataCenter::changeAvatarAsync(const QByteArray &imageBytes)
{
    _netClient.changeAvatar(_loginSessionId, imageBytes);
}

void DataCenter::resetAvatar(const QByteArray &avatar)
{
    if (_myself == nullptr)
    {
        return;
    }
    _myself->avatar = makeIcon(avatar);
}

UserInfo *DataCenter::findFriendById(const QString &userId)
{
    if (_friendList == nullptr)
        return nullptr;

    for (auto &f : *_friendList)
    {
        if (f.userId == userId)
        {
            return &f;
        }
    }
    return nullptr;
}

void DataCenter::deleteFriendAsync(const QString &userId) { _netClient.deleteFriend(_loginSessionId, userId); }

void DataCenter::removeFriend(const QString &userId)
{
    // 遍历 friendList 删除其中匹配的元素
    if (_friendList == nullptr || _chatSessionList == nullptr)
    {
        return;
    }
    _friendList->removeIf(
        [=](const UserInfo &userInfo)
        {
            // 返回 true 删除
            return userInfo.userId == userId;
        });

    // 考虑会话列表
    // 没有好友不能保留会话，需要进行会话删除
    _chatSessionList->removeIf(
        [=](const ChatSessionInfo &chatSessionInfo)
        {
            if (chatSessionInfo.userId == "")
            {
                // 如果是群聊，无需删除
                return false;
            }
            if (chatSessionInfo.userId == userId)
            {
                // 如果删除的会话正好是用户当前选中的会话，就要在界面上把当前会话的内容清空
                if (chatSessionInfo.chatSessionId == this->_currentChatSessionId)
                {
                    emit this->clearCurrentSession();
                }
                return true;
            }
            return false;
        });
}

void DataCenter::addFriendApplyAsync(const QString &userId) { _netClient.addFriendApply(_loginSessionId, userId); }

} // namespace model
